﻿using System.Collections;
using KinonekoSoftware.CodariaDB.Documents.Abstractions;
using KinonekoSoftware.Extensions.Collections;

namespace KinonekoSoftware.CodariaDB.Documents.Index
{
    public sealed class InstanceCacheEngine : _Delta<InstanceCache>, IBsonEngine, IInstanceAssemblyFactory
    {
        //-------------------------------------------------------------
        //
        //          CachedList
        //
        //-------------------------------------------------------------

        private class CachedList : ICollection<InstanceCache>
        {
            private readonly Dictionary<string, InstanceCache> _indexMap   = new Dictionary<string, InstanceCache>();
            private readonly List<InstanceCache>               _collection = new List<InstanceCache>();
            private readonly object                            _sync       = new object();

            public bool TryGetValue(string id, out InstanceCache value)
            {
                lock (_sync)
                {

                    if (_indexMap.TryGetValue(id, out var value2))
                    {
                        value = value2;
                        return true;
                    }
                }

                value = null;
                return false;
            }

            public IEnumerator<InstanceCache> GetEnumerator()
            {
                lock (_sync)
                {
                    return _collection.GetEnumerator();
                }
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                lock (_sync)
                {
                    return _collection.GetEnumerator();
                }
            }

            public void Add(InstanceCache item)
            {
                if (item is null)
                {
                    return;
                }

                lock (_sync)
                {
                    _collection.Add(item);
                    _indexMap.TryAdd(item.Id, item);
                }
            }

            public void Clear()
            {
                lock (_sync)
                {
                    _collection.Clear();
                    _indexMap.Clear();
                }
            }

            public bool Contains(InstanceCache item)
            {
                if (item is null)
                {
                    return false;
                }

                lock (_sync)
                {
                    return _indexMap.ContainsKey(item.Id);
                }
            }

            public void CopyTo(InstanceCache[] array, int arrayIndex)
            {
                lock (_sync)
                {
                    _collection.CopyTo(array, arrayIndex);
                }
            }

            public bool Remove(InstanceCache item)
            {
                if (item is null)
                {
                    return false;
                }

                lock (_sync)
                {
                    return _collection.Remove(item) && _indexMap.Remove(item.Id);
                }
            }

            public int Count
            {
                get
                {
                    lock (_sync)
                    {
                        return _collection.Count;
                    }
                }
            }
            public bool IsReadOnly => false;
        }


        //
        //    @ _Cache
        //    用于实现InstanceCache的缓存，这有助于提高速度、降低内存占用
        //    并且更方便的实现，可观测对象的内存一致性
        private readonly Dictionary<InstanceType, CachedList> _Cache = new Dictionary<InstanceType, CachedList>();
        private readonly Dictionary<string, CachedList> _CustomCache = new Dictionary<string, CachedList>();



        //-------------------------------------------------------------
        //
        //          Implement Methods
        //
        //-------------------------------------------------------------

        ILiteCollection<BsonDocument> IBsonEngine.GetLowLevelDB() => _context.Repository
                                                                             .Get<BsonDocument>(GetCollectionName());

        /// <summary>
        /// 注册组装工厂
        /// </summary>
        /// <param name="map">工厂字典</param>
        void IInstanceAssemblyFactory.Register(IDictionary<Type, IInstanceAssemblyFactory> map)
        {
            map?.Add(typeof(Factory_MSG<InstanceCache>), this);
        }

        /// <summary>
        /// 取消注册组装工厂
        /// </summary>
        /// <param name="map">工厂字典</param>
        void IInstanceAssemblyFactory.Unregister(IDictionary<Type, IInstanceAssemblyFactory> map)
        {
            map?.Remove(typeof(Factory_MSG<InstanceCache>));
        }

        /// <summary>
        /// 实现对消息的装配
        /// </summary>
        /// <param name="msg">要装配的消息</param>
        void IInstanceAssemblyFactory.Assembly(Factory_MSG msg)
        {
            if (msg is not Factory_MSG<InstanceCache> msg2)
            {
                return;
            }

            if (string.IsNullOrEmpty(msg2.Id))
            {
                return;
            }

            var type = (InstanceType)msg2.Type;

            //
            //
            msg2.Instance = Get(type, msg2.Id);
        }

        //-------------------------------------------------------------
        //
        //          Override Methods
        //
        //-------------------------------------------------------------

        public override string GetCollectionName() => CollectionNames.Cache_InstanceDB;


        protected internal override void OnDatabaseClosingInternal(ILogger logger)
        {
            base.OnDatabaseClosingInternal(logger);

            _Cache.Clear();
        }


        //-------------------------------------------------------------
        //
        //          Public Methods
        //
        //-------------------------------------------------------------

        public void AddCache(InstanceCache cache)
        {
            if (cache is null)
            {
                return;
            }

            

            if (cache.UseCustomType)
            {
                if (_CustomCache.TryGetValue(cache.CustomType, out var cached))
                {
                    cached.Add(cache);
                }
            }
            else
            {
                if (_Cache.TryGetValue(cache.Type, out var cached))
                {
                    cached.Add(cache);
                }
            }

            ((IGlossaryOperation)cache).Add(this);
            Modified(cache);
            InsideDB.Insert(cache);

        }

        public void UpdateCache(InstanceCache cache)
        {
            if (cache is null)
            {
                return;
            }

            cache.TimeOfModified = DateTime.Now;
            InsideDB.Update(cache);

            ((IGlossaryOperation)cache).Update(this);
            Modified();
        }

        public void RemoveCache(InstanceCache cache, bool softDelete = true)
        {
            if (cache is null)
            {
                return;
            }

            if (softDelete)
            {
                cache.IsDeleted      = true;
                cache.TimeOfModified = DateTime.Now;
                InsideDB.Update(cache);

            }
            else
            {
                InsideDB.Delete(cache.Id);

            }

            if (_Cache.TryGetValue(cache.Type, out var cached))
            {
                cached.Remove(cache);
            }

            //
            //
            ((IGlossaryOperation)cache).Remove(this);

            //
            //
            Queue.OnNext(new RemoveOP_InstanceCacheMSG
            {
                Id = cache.Id,
            });
            
            //
            //
            Modified();
        }

        public bool Has(string id) => InsideDB.HasID(id);

        public bool Any(InstanceType type) => InsideDB.Find(x => x.Type == type).Any();

        #region Get

        /// <summary>
        /// 未缓存Cache命中策略的Get
        /// </summary>
        /// <param name="id">设定的ID</param>
        /// <returns></returns>
        public InstanceCache Get(string id) => string.IsNullOrEmpty(id) ? null : InsideDB.FindById(id);

        /// <summary>
        /// 缓存命中的Get策略
        /// </summary>
        /// <param name="type">设定的类型</param>
        /// <param name="id">设定的ID</param>
        /// <returns></returns>
        public InstanceCache Get(InstanceType type, string id)
        {
            if (_Cache.TryGetValue(type, out var cached) &&
                cached.TryGetValue(id, out var hint))
            {
                return hint;
            }

            return InsideDB.FindById(id);
        }

        public void Invalidate(InstanceType type)
        {
            _Cache.Remove(type);
        }


        /// <summary>
        /// 带缓存策略的Get
        /// </summary>
        /// <param name="type">设定的类型</param>
        /// <returns></returns>
        public ICollection<InstanceCache> Get(InstanceType type)
        {
            if (!_Cache.TryGetValue(type, out var cached))
            {
                cached = new CachedList();
                cached.AddMany(InsideDB.Find(x => x.Type == type && !x.IsDeleted), true);
                _Cache.Add(type, cached);
            }

            return cached;
        }
        
        

        /// <summary>
        /// 带缓存策略的Get
        /// </summary>
        /// <param name="type">设定的类型</param>
        /// <returns></returns>
        public ICollection<InstanceCache> GetCustomIndexers(string type)
        {
            if (!_CustomCache.TryGetValue(type, out var cached))
            {
                cached = new CachedList();
                cached.AddMany(InsideDB.Find(x => x.UseCustomType && x.CustomType == type && !x.IsDeleted), true);
                _CustomCache.Add(type, cached);
            }

            return cached;
        }
        
        

        public IEnumerable<InstanceCache> GetRemovedInstances() => InsideDB.Find(x => x.IsDeleted);
        
        public IEnumerable<InstanceCache> Get(IEnumerable<InstanceType> types, ISet<string> excludeList)
        {
            var list = new List<InstanceCache>(64);

            foreach (var type in types)
            {
                list.AddRange(Get(type).Where(x => !excludeList.Contains(x.Id)));
            }

            return list;
        }


        /// <summary>
        /// Get
        /// </summary>
        /// <returns></returns>
        public IEnumerable<InstanceCache> GetGeographies()
        {
            return InsideDB.Find(x => x.Type == InstanceType.Geography && !x.IsDeleted);
        }

        public IEnumerable<InstanceCache> GetUnderlyingGeography(InstanceCache cache)
        {
            if (cache is null)
            {
                return null;
            }

            var id = cache.Id;

            //
            //
            return InsideDB.Find(
                x => x.Type == InstanceType.Geography &&
                     !x.IsDeleted                     &&
                     x.ParentID == id);
        }


        /// <summary>
        /// Get
        /// </summary>
        /// <returns></returns>
        // public IEnumerable<InstanceCache> GetBiologyClasses()
        // {
        //     return InsideDB.Find(x => x.Type == InstanceType.BiologyClass && !x.IsDeleted);
        // }
        //
        // public IEnumerable<InstanceCache> GetBiologyClasses(InstanceCache cache)
        // {
        //     if (cache is null)
        //     {
        //         return null;
        //     }
        //
        //     var id = cache.Id;
        //
        //     //
        //     //
        //     return InsideDB.Find(
        //         x => x.Type == InstanceType.BiologyClass &&
        //              !x.IsDeleted                        &&
        //              x.ParentID == id);
        // }

        #endregion


        public override string EngineID => EngineNames.Cache_Instance;

        public int Count() => InsideDB.Count(x => !x.IsDeleted);
    }
}